package agent.timemonitor;

import javassist.CannotCompileException;
import javassist.ClassPool;
import javassist.CtClass;
import javassist.CtMethod;

import java.lang.instrument.ClassFileTransformer;
import java.lang.instrument.IllegalClassFormatException;
import java.security.ProtectionDomain;
import java.util.Arrays;
import java.util.logging.LogManager;
import java.util.logging.Logger;

/**
 * agent.timemonitor.MethodTimeMonitorTranformer
 *
 * @author cyril
 * @date 2022/3/8 11:18 下午
 * <p>
 * 日进一寸 日拱一卒
 */
public class MethodTimeMonitorTranformer implements ClassFileTransformer {

    private static Logger logger = LogManager.getLogManager().getLogger(MethodTimeMonitorTranformer.class.getName());

    /** The internal form class name of the class to transform */
    private String targetClassName;
    /** The class loader of the class we want to transform */
    private ClassLoader targetClassLoader;

    //需要监控的方法
    private static final String WITHDRAW_MONEY_METHOD = "run";

    public MethodTimeMonitorTranformer(String targetClassName, ClassLoader targetClassLoader) {
        this.targetClassName = targetClassName;
        this.targetClassLoader = targetClassLoader;
    }

    public MethodTimeMonitorTranformer() {
    }

    @Override
    public byte[] transform(ClassLoader loader, String className, Class<?> classBeingRedefined, ProtectionDomain protectionDomain, byte[] classfileBuffer) throws IllegalClassFormatException {
        //  https://www.jianshu.com/p/1e2d970e3661

        System.out.println("[Agent] Transforming class: " + className);

        byte[] byteCode = classfileBuffer;
//        String finalTargetClassName = this.targetClassName.replaceAll("\\.", "/"); //replace . with /
//        if (!className.equals(finalTargetClassName)) {
//            return byteCode;
//        }

//        boolean doTransform = className.equals(finalTargetClassName) && loader.equals(targetClassLoader);
        boolean doTransform = className.contains("appdemo");
        System.out.println(className + ", doTransform: " + doTransform);
        if (doTransform) {
//            logger.info("......[Agent] Transforming class" + className);
            System.out.println(".............." + className);
            try {
                ClassPool cp = ClassPool.getDefault();
                CtClass cc = cp.get(className.replace("/", "."));
                CtMethod[] declaredMethods = cc.getDeclaredMethods();
                for (CtMethod m : declaredMethods) {
                    System.out.println(m.getName());
                    transformMethod(m);
                }

                byteCode = cc.toBytecode();
                cc.detach();
            } catch (Throwable e) {
                System.out.println("!!!!!  Exception: " + e.getMessage() + "  " + e.getClass().getName());
                System.out.println("!!!!!  Exception: " + Arrays.toString(Arrays.stream(Arrays.stream(e.getStackTrace()).toArray()).toArray()));
//                logger.info("Exception: " + e.getMessage());
            }
        }
        return byteCode;
    }

    private void transformMethod(CtMethod m) throws CannotCompileException {
        // 开始时间
        m.addLocalVariable("startTime", CtClass.longType);
        m.insertBefore("startTime = System.currentTimeMillis();");

        StringBuilder endBlock = new StringBuilder();
        // 结束时间
        m.addLocalVariable("endTime", CtClass.longType);
        endBlock.append("endTime = System.currentTimeMillis();");
        // 时间差
        m.addLocalVariable("opTime", CtClass.longType);
        endBlock.append("opTime = endTime-startTime;");

        // 打印方法耗时
        endBlock.append("System.out.println(\"方法执行耗时: \" + opTime + \" ms\");");
        m.insertAfter(endBlock.toString());
    }

}
